package com.lagou.binding;

import com.lagou.pojo.MappedStatement;
import com.lagou.sqlSession.SqlSession;

import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.*;
import java.util.List;

public class MapperProxy<T>  implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;

    /**
     * SqlSession 对象
     */
    private final SqlSession sqlSession;
    /**
     * Mapper 接口
     */
    private final Class<T> mapperInterface;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果是 Object 定义的方法，直接调用
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);

            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw t;
        }

        // 底层都还是去执行JDBC代码 //根据不同情况，来调用selctList或者selectOne
        // 准备参数 1：statmentid :sql语句的唯一标识：namespace.id= 接口全限定名.方法名
        // 方法名：findAll
        String methodName = method.getName();
        String className = method.getDeclaringClass().getName();

        String statementId = className+"."+methodName;
        MappedStatement mappedStatement = this.sqlSession.getConfiguration().getMappedStatementMap().get(statementId);
        String commandType = mappedStatement.getCommandType();

        //select
        if ("select".equals(commandType)) {
            Type genericReturnType = method.getGenericReturnType();
            // 判断是否进行了 泛型类型参数化
            if(genericReturnType instanceof ParameterizedType){
                List<Object> objects = this.sqlSession.selectList(statementId, args);
                return objects;
            }

            return this.sqlSession.selectOne(statementId,args);
        }

        //insert、update、delete
        if ("update".equals(commandType)) {
            return this.sqlSession.update(statementId, args);
        }

        //
        throw new RuntimeException("不支持的命令类型" + commandType);
    }

    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
            throws Throwable {
        final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
                .getDeclaredConstructor(Class.class, int.class);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }
        final Class<?> declaringClass = method.getDeclaringClass();
        return constructor
                .newInstance(declaringClass,
                        MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
                .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
    }

    /**
     * Backport of java.lang.reflect.Method#isDefault()
     */
    private boolean isDefaultMethod(Method method) {
        return (method.getModifiers()
                & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
                && method.getDeclaringClass().isInterface();
    }
}
